package com.bdqn.springsecurity1.config;

import com.bdqn.springsecurity1.filter.ValidCodeFilter;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import java.io.IOException;
import java.io.PrintWriter;


@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
        //如果不想加密就返回
        //return NoOpPasswordEncoder.getInstance();现在版本已经将其移除，淘汰了
    }

    @Bean
    public UserDetailsService userDetailsService() {
        //1.使用内存数据进行认证
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        //2.创建4个用户
        //设置内存用户名和密码并赋予角色
        UserDetails user1 = User.withUsername("admin").password(passwordEncoder().encode("123")).roles("role1", "role2", "role3").build();
        UserDetails user2 = User.withUsername("user1").password(passwordEncoder().encode("123")).roles("role1").build();
        UserDetails user3 = User.withUsername("user2").password(passwordEncoder().encode("123")).roles("role2").build();
        UserDetails user4 = User.withUsername("user3").password(passwordEncoder().encode("123")).roles("role3").build();
        //3.将这4个用户添加到内存中
        manager.createUser(user1);
        manager.createUser(user2);
        manager.createUser(user3);
        manager.createUser(user4);
        return manager;
    }

    @Bean
    WebSecurityCustomizer webSecurityCustomizer() {
        //忽略这些静态资源（不拦截）
        return (web) -> web.ignoring().requestMatchers("/js/**", "/css/**", "/images/**");
    }

    @Bean
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.addFilterBefore(validCodeFilter, UsernamePasswordAuthenticationFilter.class);
        //开启登录配置
        httpSecurity.authorizeHttpRequests()
                //允许直接访问的路径
                .requestMatchers("/", "/index","/validcode").permitAll()
                //其他任何请求都必须经过身份验证
                .anyRequest().authenticated();
        //开启表单验证
        httpSecurity.formLogin()
                //跳转到自定义登录页面
                .loginPage("/toLogin")
                //自定义表单的用户名的name属性，默认为username
                .usernameParameter("username")
                //自定义表单的密码的name属性，默认为password
                .passwordParameter("password")
                //表单请求的地址，使用Security定义好的/login,并且与自定义表单的action一致
                .loginProcessingUrl("/login")
                //如果登录失败跳转到
                //.failureUrl("/toLogin/error")
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.setContentType("application/json;charset=utf-8");
                        PrintWriter out = response.getWriter();
                        String json = "{\"status\":\"ok\",\"msg\":\"登录成功\"}";
                        out.write(json);
                    }
                })
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                        response.setContentType("application/json;charset=utf-8");
                        PrintWriter out = response.getWriter();
                        String json = "{\"status\":\"error\",\"msg\":\"" + exception.getMessage() + "\"}";
                        out.write(json);
                    }
                })
                //允许访问登录有关的路径
                .permitAll();
        //开启注销
        //注销后跳转到index页面
        //httpSecurity.logout().logoutSuccessUrl("/index");
        httpSecurity.logout().logoutSuccessHandler(new LogoutSuccessHandler() {
            @Override
            public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                response.setContentType("application/json;charset=utf-8");
                PrintWriter out = response.getWriter();
                String json = "{\"status\":\"ok\",\"msg\":\"退出登录\"}";
                out.write(json);
            }
        });
        //关闭csrf
        httpSecurity.csrf().disable();
        //记住我
        httpSecurity.rememberMe();
        return httpSecurity.build();
    }

    @Autowired
    ValidCodeFilter validCodeFilter;
}
